# -*- coding: utf-8 -*-
# @Time : 2022/5/27 18:02
# @Author : x30003433
import hashlib
import logging
import os
import time

import paramiko
import tkinter.messagebox
from tkinter import *
import requests
from xml.dom.minidom import Document
from tkinter import filedialog

VER_CFG_NAME = "vercfg.xml"
CRL_DATA = "crldata.crl"
VER_CFG_CMS = "vercfg.xml.cms"


def init_log(log_path):
    if os.path.exists(log_path):
        mode = 'a'
    else:
        mode = 'w'
    logging.basicConfig(  # 针对 basicConfig 进行配置(basicConfig 其实就是对 logging 模块进行动态的调整，之后可以直接使用)
        level=logging.INFO,  # INFO 等级以下的日志不会被记录
        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  # 日志输出格式
        filename='sign.log',  # 日志存放路径(存放在当前相对路径)
        filemode=mode,  # 输入模式；如果当前我们文件已经存在，可以使用 'a' 模式替代 'w' 模式
        # 与文件写入的模式相似，'w' 模式为没有文件时创建文件；'a' 模式为追加内容写入日志文件
    )

    return logging


log = init_log(os.path.join(os.getcwd(), 'sign.log'))


def selectPath_file():
    sel_path = filedialog.askdirectory()
    path.insert(0, sel_path)


# 校验数据
def check():
    log.info('---check enter')
    # 1.先校验对应的界面的数据是否为空
    handle_path = path.get()
    xml_vendor = e1.get()
    xml_type = e2.get()
    xml_version = e3.get()
    user = e4.get()
    if handle_path == "":
        tkinter.messagebox.showerror(title='错误', message='路径为空，请检查路径选择！')
        return False
    if xml_vendor == "":
        tkinter.messagebox.showerror(title='错误', message='Vendor为空，请检查Vendor输入！')
        return False
    if xml_type == "":
        tkinter.messagebox.showerror(title='错误', message='Type为空，请检查Type输入！')
        return False
    if xml_version == "":
        tkinter.messagebox.showerror(title='错误', message='Version为空，请检查Version输入！')
        return False
    if user == "":
        tkinter.messagebox.showerror(title='错误', message='用户为空，请检查用户输入！')
        return False
    log.info(
        "path:{0}, vendor:{1}, type:{2}, version:{3}, user:{4}".format(handle_path, xml_vendor, xml_type, xml_version,
                                                                       user))
    # 2.检查对应路径下是否包含三个签名文件。

    try:
        if os.path.exists(os.path.join(handle_path, VER_CFG_NAME)):
            os.remove(os.path.join(handle_path, VER_CFG_NAME))
        if os.path.exists(os.path.join(handle_path, CRL_DATA)):
            os.remove(os.path.join(handle_path, CRL_DATA))
        if os.path.exists(os.path.join(handle_path, VER_CFG_CMS)):
            os.remove(os.path.join(handle_path, VER_CFG_CMS))
    except:
        log.info('remove old cms exception')
        tkinter.messagebox.showerror(title='错误', message='删除老的签名文件失败！')
        return
    log.info('check success~~~')
    return True


# 循环遍历文件下的所有文件并加入到文件列表
def scanner_file(target_path, file_list):
    files = os.listdir(target_path)
    for f in files:
        real_url = os.path.join(target_path, f)
        if os.path.isfile(real_url):
            file_list.append(real_url)
        elif os.path.isdir(real_url):
            scanner_file(real_url, file_list)
        else:
            log.error("other")


# 创建xml文件
def create_xml(file_path, files_sha_map):
    doc = Document()

    product = doc.createElement("Product")
    doc.appendChild(product)

    vendor = doc.createElement("Vendor")
    txt = doc.createTextNode(e1.get())
    vendor.appendChild(txt)
    product.appendChild(vendor)

    typ = doc.createElement("Type")
    txt = doc.createTextNode(e2.get())
    typ.appendChild(txt)
    product.appendChild(typ)

    version = doc.createElement("Version")
    txt = doc.createTextNode(e3.get())
    version.appendChild(txt)
    product.appendChild(version)

    for key, value in files_sha_map.items():
        fi = doc.createElement("File")

        k = doc.createElement("FilePath")
        txt = doc.createTextNode(key)
        k.appendChild(txt)
        fi.appendChild(k)

        v = doc.createElement("SHAValue")
        txt = doc.createTextNode(value)
        v.appendChild(txt)
        fi.appendChild(v)

        product.appendChild(fi)
    f = open(file_path, 'w')
    doc.writexml(f, indent='', newl='\n', addindent='\t', encoding='UTF-8')
    f.close()


# 生成vercfg.xml文件
def init_ver_cfg():
    log.info('init_ver_cfg enter')
    # 1.遍历目标文件下的所有文件
    handle_path = path.get()
    file_list = []
    scanner_file(handle_path, file_list)
    log.debug("file_list:{0}".format(file_list))
    log.info("scanner_file success~~~")

    # 2.计算所有文件的sha256值
    sha_map = {}
    for i in file_list:
        with open(i, 'rb') as f:
            sha256obj = hashlib.sha256()
            sha256obj.update(f.read())
            hash_value = sha256obj.hexdigest()
            # key为相对路径的文件名字
            cur_file = i[len(handle_path) + 1:]
            cur_file = cur_file.replace("\\", '/')
            log.info("file:{0},sha:{1}".format(cur_file, hash_value))
            sha_map[cur_file] = hash_value
    log.debug("sha_map:{0}".format(sha_map))
    log.info("calculate sha256 success~~~")
    # 3.生成xml文件
    create_xml(os.path.join(handle_path, VER_CFG_NAME), sha_map)
    log.info('~~~init_ver_cfg success~~~')


def remove_upload_file(ssh_obj):
    log.info("remove_upload_file enter")
    # 0.删除服务器上input目录下的所有文件
    try:
        ssh_obj.exec_command("rm -rf /usr1/{0}/input/*".format(e4.get()))
    except EXCEPTION as e:
        log.info("delete input fail,exception:{0}".format(e))
        raise RuntimeError("delete input fail")
    log.info("delete input success~~~")

    # 1. 将生成的xml放在input目录下
    tran = ssh_obj.get_transport()
    sftp = paramiko.SFTPClient.from_transport(tran)
    try:
        sftp.put(os.path.join(path.get(), VER_CFG_NAME), "/usr1/{0}/input/{1}".format(e4.get(), VER_CFG_NAME))
    except EXCEPTION as e:
        log.info("send xml to input fail,exception:{0}".format(e))
        raise RuntimeError("send xml to input fail")
    sftp.close()
    log.info("send xml to input success~~~")


def get_other_file(ssh_obj):
    tran = ssh_obj.get_transport()
    sftp = paramiko.SFTPClient.from_transport(tran)
    # 签名需要时间，所以加入等待
    while len(sftp.listdir("/usr1/{0}/input/".format(e4.get()))) < 3:
        log.info("cur file:{0}".format(sftp.listdir("/usr1/{0}/input/".format(e4.get()))))
        time.sleep(1)

    log.info("final file:{0}".format(sftp.listdir("/usr1/{0}/input/".format(e4.get()))))
    try:
        sftp.get("/usr1/{0}/input/{1}".format(e4.get(), CRL_DATA), os.path.join(path.get(), CRL_DATA))
        log.info("crldata.crl get success~~~")
        sftp.get("/usr1/{0}/input/{1}".format(e4.get(), VER_CFG_CMS), os.path.join(path.get(), VER_CFG_CMS))
        log.info("vercfg.xml.cms get success~~~")
    except EXCEPTION as e:
        log.info("get_other_file fail,exception:{0}".format(e))
        raise RuntimeError("get_other_file fail")
    sftp.close()


# 获取三个签名文件放到输入目录
def get_all_file():
    log.info("get_all_file enter")
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect("10.29.172.239", 22, "guest", "huawei@123")
    # 0.删除对应服务器input目录下的所有文件
    try:
        remove_upload_file(ssh)
    except RuntimeError as e:
        log.error(e)
        return False
    # 2.发送post接口
    url = "http://10.29.172.239:8080/jenkins/job/SigService_{0}/build?delay=0sec".format(e4.get())
    log.info("url:{0}".format(url))
    res = requests.post(url, data=None)
    log.info("res.Location :{0};res.status_code:{1}".format(res.headers['Location'], res.status_code))
    if res.status_code != 200 and res.status_code != 201:
        tkinter.messagebox.showerror(title='错误', message='发送接口失败！')
        log.error("send interface fail")
        return False
    log.info("send interface success~~~")
    # 3. 将文件取回来放在目标路径下
    try:
        get_other_file(ssh)
    except RuntimeError as e:
        log.error(e)
        return False
    ssh.close()
    return True


def handle():
    log.info("---handle start")
    if not check():
        log.error("handle fail")
        path.delete(0, END)
        return
    init_ver_cfg()
    if not get_all_file():
        log.error("handle fail")
        path.delete(0, END)
        return
    tkinter.messagebox.showinfo(title='提示', message='处理成功！')
    log.info("handle success~~~")
    path.delete(0, END)


if __name__ == '__main__':
    root = Tk()
    root.title('签名小工具v1.0')
    root.geometry('310x310')

    Label(root, text='路径:').grid(row=0, column=0)
    path = Entry(root)
    path.grid(row=0, column=1, padx=10, pady=5)
    Button(root, text="路径选择", command=selectPath_file).grid(row=0, column=2, columnspan=2, padx=10, pady=5)

    Label(root, text='Vendor:').grid(row=1, column=0)
    Label(root, text='Type:').grid(row=2, column=0)
    Label(root, text='Version:').grid(row=3, column=0)
    Label(root, text='用户:').grid(row=4, column=0)

    e1 = Entry(root)
    e1.grid(row=1, column=1, padx=10, pady=5)
    e1.insert(0, 'HUAWEI')
    e2 = Entry(root)
    e2.grid(row=2, column=1, padx=10, pady=5)
    e2.insert(0, 'CSP')
    e3 = Entry(root)
    e3.grid(row=3, column=1, padx=10, pady=5)
    e3.insert(0, '23.0.0.B229')
    e4 = Entry(root)
    e4.grid(row=4, column=1, padx=10, pady=5)
    e4.insert(0, 'x30003433')

    Button(root, text="处理", command=handle).grid(row=1, column=2, padx=10, pady=5)

    t = Text(root, width=40, height=9)
    t.grid(row=5, columnspan=4, padx=10, pady=5)
    tip = "注意：\n1.Vendor、Type和Version三个值是在version.xml对应的值，根据实际情况填写即可。\n" \
          "2.用户为对应的Jenkins上面的用户。设置请参考：http://3ms.huawei.com/km/blogs/details/12322745。\n" \
          "3.选择好路径后点击处理，即可在对应路径生成三个签名文件。\n"
    t.insert('1.0', tip)
    t.config(state=DISABLED)
    root = mainloop()
